﻿using UnityEngine;
using UnityEngine.SceneManagement;
using System.IO;
using System.Collections;
using System.Collections.Generic;

public class LoadManager : ModuleBase<LoadManager>
{
    const string ATLAS_NAME = "atlas_tp";
    public const string PATH_SHADER = "Assets/Shader/";

    public delegate void LoadedCallback(object data);

    // ------------------------------------------------------------------------------------------
    // 加载任务
    LoadTask m_task;
    float m_lastClear = 0;  // 上一次清除时间

    List<string> m_searchPaths = new List<string>();

    // ------------------------------------------------------------------------------------------
    AssetBundleManifest m_manifest = null;
    Dictionary<string, AssetBundleCache> m_AssetBundleCaches = new Dictionary<string, AssetBundleCache>();  // 缓存队列

    HashSet<string> m_bundleNames = new HashSet<string>();
    List<string> m_bundles = new List<string>();
    HashSet<string> m_persistentABs = new HashSet<string>();		//常驻内存的AssetBundle，此列表中存在的ab，忽略load接口传入的persistent状态值
    HashSet<string> m_compressABs = new HashSet<string>();          //首次进入就解压的AssetBundle
    List<string> m_compressABFolders = new List<string>();          //首次进入就解压的AssetBundle 文件夹
    Dictionary<string, string> m_bundleDatas = new Dictionary<string, string>();

    // ------------------------------------------------------------------------------------------
    // Shader
    AssetBundleCache m_abShaser = null;
    Dictionary<string, Shader> m_shaders = new Dictionary<string, Shader>();

    bool m_HasCompileErrorShader = false;
    bool m_inLoadShaderVariant = false;
    ShaderVariantCollection m_ShaderVariantCollection;

    // ------------------------------------------------------------------------------------------
    // atlas
    HashSet<string> m_spriteBundles = new HashSet<string>();
    //Dictionary<string, AtlasData> m_atlas = new Dictionary<string, AtlasData>();

    public LoadManager()
    {
        if (Config.DATA_UNCOMPRESS_INRUNTIME)
        {
            AddCompressAB();
        }
        AddSearchFilePath(Config.UPDATE_DATA_PATH);
        AddSearchFilePath(Config.UNPACK_DATA_PATH);

        m_task = new LoadTask();
        Framework.Instance.AddUpdate(this);
    }

    public override void Init()
    {
        ClearForRestart();
        if (Config.Instance.UseAssetBundle)
        {
            LoadManifest(null);
        }
    }

    public override void Update(float dt)
    {
        m_task.Update(dt);
        //UpdateAssetBundleCache();
    }

    public void Clear()
    {
        m_task.Clear();

        ClearAssetBundleCache(true, false, false);

        Resources.UnloadUnusedAssets();
        if (Config.Instance.ManualGC)
        {
            System.GC.Collect();
        }
    }

    public void ClearForRestart()
    {
        ClearAssetBundleCache(false, false, true);
        Resources.UnloadUnusedAssets();
    }

    void OnStartLoad(EventBase evt)
    {
        CallbackEvent _evt = evt as CallbackEvent;

        if (Config.Instance.UseAssetBundle)
        {
            LoadManifest(_evt.OnFinish);
        }
        else
        {
            _evt.OnFinish();
        }
    }

    public List<string> BundleNames
    {
        get
        {
            return m_bundles;
        }
    }

    // 加载资源清单
    void LoadManifest(System.Action onCompleted)
    {
        // 加载模块初始化开始
        SDKReportHelper.DoReport(ReportType.InitLoadModule);

        if (m_manifest != null)
        {
            Object.DestroyImmediate(m_manifest, true);
            m_manifest = null;
        }

        m_bundleNames.Clear();
        m_bundles.Clear();

        string manifestName = Config.MANIFEST_NAME;
        LoadAssetBundle(manifestName, (data) =>
        {
            AssetBundleCache ab = data as AssetBundleCache;
            if (ab != null)
            {
                m_manifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            }

            if (onCompleted != null)
            {
                onCompleted();
            }

            UnloadAssetBundle(manifestName, true);
            if (m_manifest != null)
            {
                string[] bundles = m_manifest.GetAllAssetBundles();

                for (int i = 0; i < bundles.Length; ++i)
                {
                    if (Config.DATA_UNCOMPRESS_INRUNTIME)
                    {
                        if (m_persistentABs.Contains(bundles[i]) || m_compressABs.Contains(bundles[i]))
                        {
                            m_bundles.Add(bundles[i]);
                        }
                        else
                        {
                            for (int j = 0; j < m_compressABFolders.Count; j++)
                            {
                                if (bundles[i].Contains(m_compressABFolders[j]))
                                {
                                    m_bundles.Add(bundles[i]);
                                }
                            }
                        }
                    }
                    else
                    {
                        m_bundles.Add(bundles[i]);
                    }
                    m_bundleNames.Add(bundles[i]);
                }
            }
        }, false, false, false);

        //LoadBundleData ();
        //LoadShader();

        // 加载模块初始化完成
        SDKReportHelper.DoReport(ReportType.InitLoadModuleFinish);
    }

    // ------------------------------------------------------------------------------------------
    void LoadBundleData()
    {

    }

    public string GetBundlePath(string path, string defaultPath = "")
    {
        path = path.Replace("/", "_").ToLower();
        string newpath;
        if (!m_bundleDatas.TryGetValue(path, out newpath))
            newpath = defaultPath;

        return newpath;
    }

    // ------------------------------------------------------------------------------------------
    // 预加载Shaders(常驻)
    void LoadShader()
    {
        m_shaders.Clear();

        m_HasCompileErrorShader = false;
        m_inLoadShaderVariant = false;
        m_ShaderVariantCollection = null;

        LoadBundle("shaders", (data) =>
        {
            m_abShaser = data as AssetBundleCache;
            AssetBundle abShader = null;
            if (m_abShaser != null)
            {
                abShader = m_abShaser.Bundle;
            }
        }, false, true);
    }

    // 预热shader变种
    public void WarmUpShaderVariants(string name, LoadedCallback onLoaded = null)
    {
        if (!Config.Instance.UseAssetBundle)
        {
            return;
        }

        if (m_inLoadShaderVariant)
        {
            return;
        }

        if (m_ShaderVariantCollection == null)
        {
            m_inLoadShaderVariant = true;

            Debug.Log("~~~~~~~~~~~~begin warnup shader:" + Time.time);
            // 同步加载ab
            LoadBundle("shaders_variants", (data) =>
            {
                AssetBundleCache cache = data as AssetBundleCache;
                if (cache == null)
                {
                    m_inLoadShaderVariant = false;
                    if (onLoaded != null)
                    {
                        onLoaded(null);
                    }
                    return;
                }

                string code = PlayerPrefs.GetString("__INNER_APP_SHADER_VARIANT_CODE__", "");
                bool isFirst = !string.Equals(code, Config.Instance.config.PackageCode);
                isFirst = false;
                // 加载asset,安装完首次使用异步,其它情况用同步加载
                LoadBundleAsset(cache.Bundle, name, typeof(ShaderVariantCollection), (_data) =>
                {
                    m_ShaderVariantCollection = _data as ShaderVariantCollection;
                    if (m_ShaderVariantCollection == null)
                    {
                        m_inLoadShaderVariant = false;
                        if (onLoaded != null)
                        {
                            onLoaded(null);
                        }
                        return;
                    }

                    m_ShaderVariantCollection.WarmUp();

                    Debug.Log("~~~~~~~~~~~~end warnup shader:" + Time.time);
                    PlayerPrefs.SetString("__INNER_APP_SHADER_VARIANT_CODE__", Config.Instance.config.PackageCode);
                    m_inLoadShaderVariant = false;

                    if (onLoaded != null)
                    {
                        onLoaded(m_ShaderVariantCollection);
                    }
                }, isFirst);
            }, false, true);
        }
        else
        {
            m_ShaderVariantCollection.WarmUp();
        }
    }

    //  进入前台尝试加载错误的shader列表
    public void ReloadErrorShaders(bool focus)
    {
        if (!focus)
        {
            return;
        }

#if !UNITY_EDITOR && UNITY_ANDROID
        if (m_HasCompileErrorShader){
            m_HasCompileErrorShader = false;
			NativeManager.Instance.CallNativeMethodName ("RestartActivity");
        }
#endif
    }

    void RefreshShader(AssetBundle ab)
    {
        if (ab.isStreamedSceneAssetBundle)
        {
            return;
        }

        Material[] materials = ab.LoadAllAssets<Material>();
        for (int i = 0; i < materials.Length; ++i)
        {
            Material mat = materials[i];
            string shaderName = mat.shader.name;
            Shader newShader = Shader.Find(shaderName);
            if (newShader != null)
            {
                mat.shader = newShader;
            }
            else
            {
                Debuger.LogWarning("unable to refresh shader: {0} in material {1} ", shaderName, mat.name);
            }
        }
    }

    public Shader GetShader(string shaderPath, string shaderName = null)
    {
        Shader shader = null;

        //ab中找不到再使用shader.find接口
        if (!string.IsNullOrEmpty(shaderName))
        {
            shader = Shader.Find(shaderName);

            if (shader != null)
            {
                Debuger.Log("Find shader from Shader.Fing...name: {0}", shaderName);
                return shader;
            }
        }

        //先从ab中加载
        if (!string.IsNullOrEmpty(shaderPath) && m_abShaser != null)
        {
            shaderPath = PATH_SHADER + shaderPath;
            if (!m_shaders.TryGetValue(shaderName, out shader))
            {
                shader = m_abShaser.LoadAsset<Shader>(shaderPath);
                //shader = Shader.Find(shader.name);
                m_shaders.Add(shader.name, shader);
            }

            if (shader != null)
            {
                Debuger.Log("Find shader from Asset...name:{0}", shaderName);
#if !UNITY_EDITOR && UNITY_ANDROID
				Debuger.Log(shaderPath + ", " +shader.name+" "+shader.isSupported);
				if(!shader.isSupported && 
					(shader.name == "UI/Default(RGB+A)" || shader.name == "UI/Default" || shader.name == "UI/DefaultETC1"))
				{
					Debuger.Log("Shader has errors!!!!");
					m_HasCompileErrorShader = true;
				}
#endif
                return shader;
            }

        }

        return shader;
    }

    public Shader GetShader(string name)
    {
        Shader shader = null;
        if (!m_shaders.TryGetValue(name, out shader))
        {
            shader = Shader.Find(name);
        }

        return shader;
    }

    // ------------------------------------------------------------------------------------------
    void AddCompressAB()
    {
        //m_compressABFolders.Add("ui_atlas_tp_");
        //m_compressABFolders.Add("art_oe_effect_ui_res_");

        //m_compressABs.Add("ui_panel_common.unity3d");
        //m_compressABs.Add("ui_panel_lr_common.unity3d");
        //m_compressABs.Add("ui_panel_eventcenter.unity3d");
        //m_compressABs.Add("ui_panel_lr_eventcenter.unity3d");
        //m_compressABs.Add("ui_panel_guide.unity3d");
        //m_compressABs.Add("ui_panel_lr_guide.unity3d");
        //m_compressABs.Add("ui_panel_item.unity3d");
        //m_compressABs.Add("ui_panel_lr_item.unity3d");
        //m_compressABs.Add("ui_panel_citybuilding.unity3d");
        //m_compressABs.Add("ui_panel_lr_citybuilding.unity3d");

        //m_compressABs.Add("ui_spine_guidenpc.unity3d");
        //m_compressABs.Add("ui_font_static_font_sfont_tr.unity3d");
        //m_compressABs.Add("ui_animation.unity3d");

        m_compressABs.Add("scenes_city.unity3d");
        m_compressABs.Add("shaders_variants.unity3d");
        m_compressABs.Add("shaders.unity3d");
        m_compressABs.Add("ui_atlas_tp_common_tr.unity3d");
        m_compressABs.Add("art_scene_city.unity3d");

        //m_compressABs.Add("art_effect_world.unity3d");
        //m_compressABs.Add("art_effect_city.unity3d");
        //m_compressABs.Add("art_effect_animation.unity3d");
        //m_compressABs.Add("art_building_city_building.unity3d");
        //m_compressABs.Add("effect_city.unity3d");

        //m_compressABs.Add("animation_2d_soldier.unity3d");
        //m_compressABs.Add("animation_2d_world.unity3d");
        //m_compressABs.Add("animation_2d_image_animation_city.unity3d");
        //m_compressABs.Add("animation_actor_city.unity3d");
        //m_compressABs.Add("animation_actor_army.unity3d");
        //m_compressABs.Add("animation_actor_world.unity3d");

        //m_compressABs.Add("city_mat.unity3d");
        //m_compressABs.Add("city_model.unity3d");
        //m_compressABs.Add("city_special.unity3d");
        //m_compressABs.Add("city_reference_prefab.unity3d");

        //m_compressABs.Add("sound_soundconfig.unity3d");
        //m_compressABs.Add("music_worldbattle.unity3d");
        //m_compressABs.Add("music_city.unity3d");

        //m_compressABs.Add("config_sorting_layer_config.unity3d");

    }

    public void AddSearchFilePath(string path)
    {
        m_searchPaths.Add(path);
    }

    public string SearchFilePath(string subpath, bool noStreamAssetPath = false, bool needSuffix = false)
    {
        if (needSuffix)
        {
            subpath = string.Format("{0}.{1}", subpath, Config.AB_SUFFIX);
        }

        string fullpath = "";
        // 优先从查找目录找
        for (int i = 0; i < m_searchPaths.Count; ++i)
        {
            fullpath = string.Format("{0}/{1}", m_searchPaths[i], subpath);
            if (File.Exists(fullpath))
            {
                return fullpath;
            }
        }

        // 不查找StreamAsset目录
        if (noStreamAssetPath)
        {
            return "";
        }

        if (!HasBundle(subpath))
        {
            // 没有这个ab
            return "";
        }
        else
        {
            return string.Format("{0}/{1}", Config.STREAMING_ASSETS_PATH, subpath);
        }
    }

    // ------------------------------------------------------------------------------------------
    // 编辑器专用加载
    // 加载Assets目录下的文件(编辑器专用,带后缀)
    public void LoadFile(string path, LoadedCallback onLoaded, bool async = true, bool inData = true)
    {
#if !UNITY_EDITOR && !UNITY_STANDALONE
		Debuger.LogError("LoadFile为编辑器专用方法!");
		return;
#endif

        string fullpath = string.Format("{0}/{1}", inData ? Config.ORIGINAL_FULLPATH : Application.dataPath, path);
        if (!CheckFileExist(fullpath, onLoaded))
        {
            return;
        }

        m_task.AddLoadTask(Loader.LoaderType.STREAM, fullpath, false, onLoaded, async);
    }

    // 加载资源(Assets目录下,带后缀)
    public void LoadAssetFile(string path, LoadedCallback onLoaded, System.Type type = null, bool async = true, bool inData = true)
    {
#if !UNITY_EDITOR && !UNITY_STANDALONE
		Debuger.LogError("LoadAsset为编辑器专用方法!");
		return;
#endif

        string fullpath = string.Format("{0}/{1}", inData ? Config.ORIGINAL_FULLPATH : Application.dataPath, path);
        if (!CheckFileExist(fullpath, onLoaded))
        {
            return;
        }

        fullpath = inData ? string.Format("Assets/{0}/{1}", Config.ORIGINAL_PATH, path) : string.Format("Assets/{0}", path);
        Debug.Log("fullpath>>" + fullpath);
        m_task.AddLoadTask(Loader.LoaderType.ASSET, fullpath, type, onLoaded, async);
    }

    // 全平台加载
    // 加载流文件(remote==false时先从persistentData读,没有找到则从streamingAssets读,带后缀)
    public void LoadStream(string path, LoadedCallback onLoaded, bool async = true, bool remote = false, bool isFullPath = false)
    {
        string fullpath = path;
        if (!remote)
        {
            if (!isFullPath)
            {
                fullpath = SearchFilePath(path);
            }
        }
        else
        {
            // 从服务器加载,一定是异步的
            async = true;
        }

        m_task.AddLoadTask(Loader.LoaderType.STREAM, fullpath, remote, onLoaded, async);
    }

    // 加载资源(Resource目录下,不带后缀)
    public void LoadResource(string path, LoadedCallback onLoaded, bool async = true)
    {
        m_task.AddLoadTask(Loader.LoaderType.RESOURCE, path, null, onLoaded, async);
    }

    // 加载关卡
    public void LoadScene(string name, LoadedCallback onLoaded, bool async)
    {
        m_task.AddLoadTask(Loader.LoaderType.SCENE, name, null, onLoaded, async);
    }

    // 卸载场景
    public void UnloadScene(string name)
    {
        SceneManager.UnloadSceneAsync(name);
    }

    public bool HasBundle(string path)
    {
        path = path.Replace("/", "_").ToLower();
        return m_bundleNames.Count == 0 || m_bundleNames.Contains(path) || string.Equals(path, Config.MANIFEST_NAME);
    }

    // 加载AssetBundle(先从persistentData读,没有找到则从streamingAssets读,不带后缀)
    public void LoadBundle(string path, LoadedCallback onLoaded, bool async = true, bool persistent = false, bool manifest = true)
    {
        string name = string.Format("{0}.{1}", path, Config.AB_SUFFIX).ToLower();
        LoadAssetBundle(name, onLoaded, async, persistent, manifest);
    }

    // 卸载AssetBundle
    public void UnloadBundle(string path, bool immediate = false)
    {
        string name = string.Format("{0}.{1}", path, Config.AB_SUFFIX);
        UnloadAssetBundle(name, immediate);
    }

    public void LoadBundleAsset(AssetBundle ab, string name, System.Type type, LoadManager.LoadedCallback onLoaded, bool async = true)
    {
        object[] param = new object[] { ab, type };
        m_task.AddLoadTask(Loader.LoaderType.BUNDLEASSET, name, param, onLoaded, async);
    }

    // 从AssetBundle中加载资源,ab路径不带后缀
    public void LoadAssetFromBundle(string path, string name, System.Type type, LoadManager.LoadedCallback onLoaded, bool async, bool persistent, bool unload = false)
    {
        string fullpath = string.Format("{0}.{1}", path, Config.AB_SUFFIX).ToLower();
        LoadAssetBundle(fullpath, (data) =>
        {
            AssetBundleCache cache = data as AssetBundleCache;
            object asset = null;
            if (cache != null)
            {
                if (!string.IsNullOrEmpty(name))
                {
                    asset = cache.LoadAsset(name, type);
                    if (!cache.Persistent && (unload || type != null && type == typeof(GameObject)))
                    {
                        cache.UnloadSelf();
                    }
                }
                else
                {
                    asset = cache.Bundle;
                }
            }

            if (asset == null)
            {
                Debuger.LogWarning("LoadAssetFromBundle, path:{0},name:{1}, asset is null", path, name);
            }

            if (onLoaded != null)
            {
                onLoaded(asset);
            }
        }, async, persistent);
    }

    // 加载资源
    public void LoadAsset(string path, string name, string ext, System.Type type, LoadedCallback onLoaded, bool async = true, bool persistent = false, bool unload = false, bool isFullPath = false)
    {
        if (Config.Instance.UseAssetBundle)
        {
            string fullpath = path;
            if (!isFullPath)
                fullpath = string.Format("{0}/{1}", path, name);
            LoadAssetFromBundle(fullpath, name, type, onLoaded, async, persistent, unload);
        }
        else
        {
            string fullpath = string.Format("{0}/{1}.{2}", path, name, ext);
            LoadAssetFile(fullpath, onLoaded, type, async, true);
        }
    }

    public void UnloadAsset(string path, string name)
    {
        if (Config.Instance.UseAssetBundle)
        {
            string fullpath = string.Format("{0}/{1}", path, name);
            UnloadBundle(fullpath);
        }
    }

    // ------------------------------------------------------------------------------------------
    // 加载AssetBundle(先从persistentData读,没有找到则从streamingAssets读,带后缀)
    void LoadAssetBundle(string path, LoadedCallback onLoaded, bool async = true, bool persistent = false, bool manifest = true)
    {
        path = path.Replace("/", "_").ToLower();

        string newpath;
        if (m_bundleDatas.TryGetValue(path, out newpath))
            path = newpath;

        if (m_persistentABs.Contains(path))
        {
            persistent = true;
        }

        if (manifest)
        {
            if (!HasBundle(path))
            {
                // Manifest里没有这个AssetBundle,说明是一个错误的路径
                if (onLoaded != null)
                {
                    if (!async)
                    {
                        onLoaded(null);
                    }
                    else
                    {
                        m_task.AddAsyncCallback(onLoaded, null);
                    }
                }
                return;
            }

            // 加载依赖
            bool asyncInFact;
            LoadDependencies(path, async, persistent, out asyncInFact);
            async = asyncInFact;
        }

        // 检查是否有缓存
        if (CheckAssetBundleCache(path, onLoaded, persistent, async))
        {
            return;
        }

        string fullpath = SearchFilePath(path);
        Debug.Log("LoadAssetBundle>>" + path + ">>" + fullpath);
        // 检查是否已经在加载（WaitLoading 中只能是异步加载，如果是同步加载，会直接出现在cache中）
        if (m_task.CheckImmediateLoad(fullpath, onLoaded))
        {
            return;
        }

        // 添加加载任务
        m_task.AddLoadTask(Loader.LoaderType.BUNDLE, fullpath, null, (data) =>
        {
            AssetBundle ab = data as AssetBundle;
            AssetBundleCache cache = null;

            if (ab != null)
            {
#if UNITY_EDITOR
                RefreshShader(ab);
#endif
                // 加载完成,添加缓存
                int refCount = m_task.GetWaitLoadingCount(fullpath) + 1;
                cache = AddAssetBundleCache(path, ab, persistent, refCount);
            }
            else
            {
                Debuger.LogWarning("========LoadAssetBundle not exist,abname:{0}", path);
            }

            // 加载回调
            if (onLoaded != null)
            {
                onLoaded(cache);
            }

            // 通知等待加载
            m_task.WaitLoadingFinish(fullpath, cache);
        }, async);
    }

    // 卸载AssetBundle
    void UnloadAssetBundle(string path, bool immediate = false)
    {
        path = path.Replace("/", "_").ToLower();
        if (RemoveAssetBundleCache(path, immediate))
        {
            UnloadDependencies(path, immediate);
        }
    }

    // 依赖
    // 加载依赖
    //asyncInFact 实际加载方式，如果依赖bundle是异步加载并且正在加载中，那么整个bundle的加载方式变成异步加载
    void LoadDependencies(string name, bool async, bool persistent, out bool asyncInFact)
    {
        asyncInFact = async;

        if (m_manifest == null)
        {
            return;
        }

        string[] dependencies = m_manifest.GetDirectDependencies(name);
        for (int i = 0; i < dependencies.Length; ++i)
        {
            LoadAssetBundle(dependencies[i], null, async, persistent);
            if (!async)
            {
                AssetBundleCache abCache = null;
                if (!m_AssetBundleCaches.TryGetValue(dependencies[i], out abCache))
                {
                    //同步加载，只要有一个被依赖ab没有加载完成，说明它正在加载，那么依赖方自己也变成异步加载，这种情况应该及其少见
                    asyncInFact = false;
                    Debuger.LogWarning(string.Format("===============LoadDependencies,{0}:{1}, change async to sync", name, dependencies[i]));
                }
            }
        }
    }

    // 卸载依赖
    void UnloadDependencies(string name, bool immediate = false)
    {
        if (m_manifest == null)
        {
            return;
        }

        string[] dependencies = m_manifest.GetDirectDependencies(name);
        for (int i = 0; i < dependencies.Length; ++i)
        {
            UnloadAssetBundle(dependencies[i], immediate);
        }
    }

    // 缓存
    // 添加AssetBundle缓存
    AssetBundleCache AddAssetBundleCache(string name, AssetBundle bundle, bool persistent, int refCount)
    {
        if (m_AssetBundleCaches.ContainsKey(name))
        {
            return m_AssetBundleCaches[name];
        }

        AssetBundleCache cache = new AssetBundleCache(name, bundle, persistent, refCount);
        m_AssetBundleCaches.Add(name, cache);
        return cache;
    }

    // 移除AssetBundle缓存
    bool RemoveAssetBundleCache(string name, bool immediate = false)
    {
        AssetBundleCache item = null;
        if (!m_AssetBundleCaches.TryGetValue(name, out item))
        {
            return false;
        }

        --item.ReferencedCount;

        if ((Config.Instance.AssetCacheTime == 0 || immediate) && item.IsCanRemove)
        {
            UnloadAssetBundleCache(name);
        }

        return true;
    }

    // 从缓存中获取AssetBundle
    AssetBundleCache GetAssetBundleCache(string name, bool persistent)
    {
        if (!m_AssetBundleCaches.ContainsKey(name))
        {
            return null;
        }

        AssetBundleCache item = m_AssetBundleCaches[name];
        ++item.ReferencedCount;
        if (persistent)
        {
            item.Persistent = true;
        }

        return item;
    }

    // 从缓存中获取Assetbundle,不增加引用
    AssetBundle GetAssetBundle(string path)
    {
        string name = string.Format("{0}.{1}", path, Config.AB_SUFFIX).ToLower();
        AssetBundleCache item = null;
        if (m_AssetBundleCaches.TryGetValue(name, out item))
        {
            return item.Bundle;
        }

        return null;
    }

    void UnloadAssetBundleCache(string key, bool isUnloadUnusedAsset = false)
    {
        AssetBundleCache item = m_AssetBundleCaches[key];
        if (item != null)
        {
            item.Unload(isUnloadUnusedAsset);
        }
        m_AssetBundleCaches.Remove(key);
    }

    // 清除AssetBundle缓存
    void ClearAssetBundleCache(bool onlyRefZero = true, bool onlyTimeout = true, bool includePersistent = false)
    {
        AssetBundleCache item = null;
        string[] keys = null;
        string key = "";

        keys = new string[m_spriteBundles.Count];
        m_spriteBundles.CopyTo(keys);
        for (int i = 0; i < keys.Length; ++i)
        {
            key = keys[i];
            if (m_AssetBundleCaches.TryGetValue(key, out item))
            {

            }
            else
            {
                m_spriteBundles.Remove(key);
            }
        }

        keys = new string[m_AssetBundleCaches.Count];
        m_AssetBundleCaches.Keys.CopyTo(keys, 0);

        for (int i = 0; i < keys.Length; ++i)
        {
            key = keys[i];
            item = m_AssetBundleCaches[key];
            if (onlyRefZero)
            {
                // 只清除引用计数为0的
                if (item.IsCanRemove && (!onlyTimeout || item.IsTimeOut))
                {
                    UnloadAssetBundleCache(key, false);
                }
            }
            else if (includePersistent || item.IsCanRemove)
            {
                UnloadAssetBundleCache(key, false);
            }
        }

        keys = null;
    }

    // 更新AssetBundle缓存(主要执行定时清理)
    void UpdateAssetBundleCache()
    {
        if (Config.Instance.AssetCacheTime == 0 || Time.realtimeSinceStartup - m_lastClear < Config.Instance.AssetCacheTime)
        {
            return;
        }

        m_lastClear = Time.realtimeSinceStartup;
        ClearAssetBundleCache(true, true, false);
    }

    // 检查是否已在缓存(是-引用计数+1,并返回true, 否-返回false)
    bool CheckAssetBundleCache(string name, LoadedCallback onLoaded, bool persistent, bool async)
    {
        AssetBundleCache ab = GetAssetBundleCache(name, persistent);
        if (ab == null)
        {
            return false;
        }

        if (onLoaded != null)
        {
            if (!async)
            {
                onLoaded(ab);
            }
            else
            {
                m_task.AddAsyncCallback(onLoaded, ab);
            }
        }
        return true;
    }

    // ------------------------------------------------------------------------------------------
    public void LoadResourceFile(string fullpath, ResourceType tp, LoadedCallback onLoaded, bool async = true)
    {
        if (File.Exists(fullpath))
        {
            LoadStream(fullpath, delegate (object _data)
            {
                LoadFromCacheOrDownloadCallback(tp, onLoaded, _data);
            }, async, false, true);
        }
        else
        {
            LoadFromCacheOrDownloadCallback(tp, onLoaded, null);
        }
    }

    public void LoadFromCacheOrDownload(string url, string path, ResourceType tp, LoadedCallback onLoaded, bool async = true, bool onlyaCache = false)
    {
        string pathFull = string.Format("{0}/{1}", Config.CACHE_PATH, path);
        if (File.Exists(pathFull))
        {
            LoadStream(pathFull, (_data) =>
            {
                LoadFromCacheOrDownloadCallback(tp, onLoaded, _data);
            }, async, false, true);
        }
        else
        {
            if (onlyaCache)
            {
                LoadFromCacheOrDownloadCallback(tp, onLoaded, null);
            }
            else
            {
                string pathURLFull = string.Format("{0}/{1}", url, path);
                LoadStream(pathURLFull, (_data) =>
                {
                    if (_data != null)
                    {
                        FileHelper.SaveBytesToFile(_data as byte[], pathFull);
                    }

                    LoadFromCacheOrDownloadCallback(tp, onLoaded, _data);
                }, true, true);
            }
        }
    }

    void LoadFromCacheOrDownloadCallback(ResourceType tp, LoadedCallback onLoaded, object data)
    {
        if (onLoaded == null)
        {
            return;
        }

        if (data == null)
        {
            onLoaded(data);
        }
        else
        {
            byte[] bytes = data as byte[];

            switch (tp)
            {
                case ResourceType.TEXTURE:
                    {
                        Texture2D texture = new Texture2D(1, 1);
                        texture.LoadImage(bytes);

                        onLoaded(texture);
                    }
                    break;

                case ResourceType.SPRITE:
                    {
                        Texture2D texture = new Texture2D(1, 1);
                        texture.LoadImage(bytes);
                        Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));

                        onLoaded(sprite);
                    }
                    break;

                default:
                    onLoaded(data);
                    break;
            }
        }
    }

    // ------------------------------------------------------------------------------------------
    bool CheckFileExist(string path, LoadedCallback onLoaded, bool isFile = true)
    {
        bool exist = false;
        if (isFile)
        {
            exist = File.Exists(path);
        }
        else
        {
            exist = Directory.Exists(path);
        }

        if (!exist)
        {
            // 不存在
            if (onLoaded != null)
            {
                onLoaded(null);
            }
            return false;
        }

        return true;
    }

    // ------------------------------------------------------------------------------------------
    // 前台加载
    public void BeginFrontLoad()
    {
        m_task.BeginFrontLoad();
    }

    public void StartFrontLoad()
    {
        m_task.StartFrontLoad();
    }

    // ------------------------------------------------------------------------------------------
    //设置AssetBundle常驻列表，在lua中调用
    public void SetPersistentABs(string[] arrAB)
    {
        m_persistentABs.Clear();
        for (int i = 0; i < arrAB.Length; i++)
        {
            string strAB = (string.Format("{0}.{1}", arrAB[i], Config.AB_SUFFIX)).ToLower();
            strAB = strAB.Replace("/", "_");
            m_persistentABs.Add(strAB);
            if (m_AssetBundleCaches != null && m_AssetBundleCaches.ContainsKey(strAB))
            {
                m_AssetBundleCaches[strAB].Persistent = true;
            }
        }
    }

    public void Dump()
    {
        {
            string str = string.Format("Dump Load Count = {0}\n", m_AssetBundleCaches.Count);
            var iter = m_AssetBundleCaches.GetEnumerator();
            while (iter.MoveNext())
            {
                AssetBundleCache item = iter.Current.Value;
                str += string.Format("{0} count={1}, persistent:{2} {3}\n", iter.Current.Key, item.ReferencedCount, item.Persistent, item.Bundle);
            }

            Debuger.Log(str);
        }
    }

    #region Sprite
    Dictionary<string, AtlasData> m_atlas = new Dictionary<string, AtlasData>();

    public void LoadSprite(string atlas, string name, LoadedCallback onLoaded, bool async = true, bool useAtlas = false, bool needMaterial = false)
    {
        bool hasUpper = false;
        for (int i = 0, len = atlas.Length; i != len; ++i)
        {
            if (char.IsUpper(atlas[i]))
            {
                hasUpper = true;
                break;
            }
        }
        if (hasUpper)
            atlas = atlas.ToLower();
        if (FindSprite(atlas, name, needMaterial, onLoaded, false))
        {
            return;
        }

        if (Config.Instance.UseAssetBundle)
        {
            LoadAtlasBundle(atlas, (data) =>
            {
                FindSprite(atlas, name, needMaterial, onLoaded);
            }, async, useAtlas);
        }
        else
        {
            if (useAtlas)
            {
                string fullpath = string.Format("{0}/{1}/{2}.prefab", Config.UI_PATH, ATLAS_NAME, atlas);
                LoadAssetFile(fullpath, (data) =>
                {
                    GameObject go = data as GameObject;
                    AddSprites(atlas, go);
                    FindSprite(atlas, name, needMaterial, onLoaded);
                }, typeof(GameObject), async, true);
            }
            else
            {
                string fullpath = string.Format("{0}/atlas/{1}/{2}.png", Config.UI_PATH, atlas, name);

                LoadAssetFile(fullpath, (data) =>
                {
                    AddSprite(atlas, data as Sprite);
                    FindSprite(atlas, name, needMaterial, onLoaded);
                }, typeof(Sprite), async, true);
            }
        }
    }

    void AddSpriteBundleName(string path)
    {
        string fullpath = string.Format("{0}.{1}", path, Config.AB_SUFFIX);
        if (!m_spriteBundles.Contains(fullpath))
        {
            m_spriteBundles.Add(fullpath);
        }
    }

    // 加载图集AssetBundle
    void LoadAtlasBundle(string atlas, LoadedCallback onLoaded, bool async = true, bool useAtlas = false, bool persistent = false)
    {
        if (useAtlas)
        {
            string fullpath = string.Format("{0}/{1}/{2}", Config.UI_PATH, ATLAS_NAME, atlas);
            LoadBundle(fullpath, (data) =>
            {
                AddSpriteBundleName(fullpath);

                if (onLoaded != null)
                {
                    onLoaded(null);
                }
            }, async, persistent);
        }
        else
        {
            string fullpath = string.Format("{0}/atlas/{1}", Config.UI_PATH, atlas);
            LoadBundle(fullpath, (data) =>
            {
                AddSpriteBundleName(fullpath);
                if (onLoaded != null)
                {
                    onLoaded(null);
                }
            }, async, persistent);
        }
    }

    string GetRealAtlasName(string atlas)
    {
        return atlas.Replace("ui_atlas_tp_", "").Replace("ui_atlas_", "");
    }

    public void AddSprites(string atlas, GameObject go)
    {
        if (go == null)
        {
            return;
        }

        AtlasHierarchy hierarchy = go.GetComponent<AtlasHierarchy>();
        if (hierarchy == null)
        {
            return;
        }

        atlas = GetRealAtlasName(atlas);

        hierarchy.atlas.InitMap();

        if (m_atlas.ContainsKey(atlas))
        {
            m_atlas[atlas] = hierarchy.atlas;
        }
        else
        {
            m_atlas.Add(atlas, hierarchy.atlas);
        }
    }

    public void AddSprites(string atlas, Sprite[] sprites)
    {
        if (sprites == null)
        {
            return;
        }

        atlas = GetRealAtlasName(atlas);
        AtlasData sd = null;
        if (!m_atlas.TryGetValue(atlas, out sd))
        {
            sd = new AtlasData();
            sd.Name = atlas;
            m_atlas.Add(atlas, sd);
        }
        sd.AddSprites(sprites);
    }

    void AddSprite(string atlas, Sprite sprite)
    {
        if (sprite == null)
        {
            return;
        }

        atlas = GetRealAtlasName(atlas);
        AtlasData sd = null;
        if (!m_atlas.TryGetValue(atlas, out sd))
        {
            sd = new AtlasData();
            sd.Name = atlas;
            m_atlas.Add(atlas, sd);
        }

        sd.AddSprite(sprite);
    }

    public void RemoveSprites(string atlas, GameObject go)
    {
        atlas = GetRealAtlasName(atlas);
        RemoveAtlas(atlas);
    }

    public void RemoveSprites(string atlas, Sprite[] sprites)
    {
        atlas = GetRealAtlasName(atlas);
        AtlasData sd = null;
        if (m_atlas.TryGetValue(atlas, out sd))
        {
            sd.RemoveSprites(sprites);

            if (sd.SpriteCount == 0)
            {
                RemoveAtlas(atlas);
            }
        }
    }

    void RemoveAtlas(string atlas)
    {
        atlas = GetRealAtlasName(atlas);
        AtlasData sd = null;
        if (m_atlas.TryGetValue(atlas, out sd))
        {
            sd.Dispose();
            m_atlas.Remove(atlas);
        }
    }

    public int GetAtlasRefCount(string atlas)
    {
        atlas = GetRealAtlasName(atlas);
        AtlasData sd = null;
        if (m_atlas.TryGetValue(atlas, out sd))
        {
            return sd.ReferenceCount;
        }

        return 0;
    }

    bool FindSprite(string atlas, string name, bool needMaterial, LoadedCallback onLoaded = null, bool alwayCallback = true)
    {
        atlas = GetRealAtlasName(atlas);
        object data = null;

        AtlasData sd = null;
        if (m_atlas.TryGetValue(atlas, out sd))
        {
            if (needMaterial)
            {
                if (sd.material == null)
                {

                }
                data = sd.GetSpriteInfo(name);
            }
            else
            {
                data = sd.GetSprite(name);
            }
        }

        if (onLoaded != null)
        {
            if (alwayCallback || data != null)
            {
                onLoaded(data);
            }
        }

        return data != null;
    }

    #endregion

    #region Emoji

    public void LoadEmoji(LoadedCallback onLoaded, bool async = true, bool useAtlas = false)
    {
        string atlas = "emoji";

        if (Config.Instance.UseAssetBundle)
        {
            LoadAtlasBundle(atlas, (data) =>
            {
                FindEmojiNames(atlas, onLoaded);
            }, async, useAtlas, true);
        }
        else
        {
#if !UNITY_EDITOR
			Debuger.LogError("LoadSprite非inBundle为编辑器专用方法");
			return;
#else
            if (useAtlas)
            {
                string fullpath = string.Format("{0}/{1}/{2}.prefab", Config.UI_PATH, ATLAS_NAME, atlas);
                LoadAssetFile(fullpath, (data) =>
                {
                    GameObject go = data as GameObject;
                    AddSprites(atlas, go);

                    FindEmojiNames(atlas, onLoaded);
                }, typeof(GameObject), async, true);
            }
            else
            {
                string fullpath = string.Format("{0}/{1}/atlas/{2}", Config.DATA_ASSETS_PATH, Config.UI_PATH, atlas);
                if (Directory.Exists(fullpath))
                {
                    List<string> files = new List<string>();
                    FileHelper.GetAllChildFiles(fullpath, "png", files);
                    for (int i = 0; i < files.Count; ++i)
                    {
                        Sprite sprite = UnityEditor.AssetDatabase.LoadAssetAtPath<Sprite>(files[i]);
                        AddSprite(atlas, sprite);
                    }
                }

                FindEmojiNames(atlas, onLoaded);
            }
#endif
        }
    }

    void FindEmojiNames(string atlas, LoadedCallback onLoaded)
    {
        string[] names = null;
        AtlasData sd = null;
        if (m_atlas.TryGetValue(atlas, out sd))
        {
            names = sd.SpriteNames;
        }

        if (onLoaded != null)
        {
            onLoaded(names);
        }
    }

    public SpriteInfo GetEmoji(string name)
    {
        SpriteInfo ret = null;

        string atlas = "emoji";

        AtlasData sd = null;
        if (m_atlas.TryGetValue(atlas, out sd))
        {
            ret = sd.GetSpriteInfo(name);
        }

        return ret;
    }
    #endregion
}